<?php

namespace My\core;

use DI\Container;
use DI\ContainerBuilder;
use DI\DependencyException;
use DI\NotFoundException;

class BeanFactory
{
    private static $env = [];       //key-value,env 配置文件
    private static Container $container;       //ioc容器

    private static $handler = [];


    //初始化函数
    public static function init()
    {
        //加载env文件
        self::$env = parse_ini_file(ROOT_PATH . '/env');
        //使用第三方库 di 来实现依赖注入;
        $builder = new ContainerBuilder();
        //$builder->useAnnotations(true);    //7.x已废弃该方法，用php8的Attributes来实现
        $builder->useAttributes(true);
        self::$container = $builder->build();   //容器初始化

        $handlePaths = glob(ROOT_PATH . '/core/annotationHandle/*.php');
        foreach ($handlePaths as $handlePath) {
            self::$handler = array_merge(self::$handler, require_once($handlePath));
        }

        //self::scanBeans();   //扫描

        $scans = [
            ROOT_PATH . "/core/frame" => "My\\core\\",
            self::getEnv('scan_dir', ROOT_PATH . '/app') => self::getEnv('scan_root_namespace', "My\\app\\")
        ];

        foreach ($scans as $dir => $namespace) {
            self::scanBeans($dir, $namespace);
        }
    }

    //获取env文件
    public static function getEnv(string $key, string $default = '')
    {
        if (isset(self::$env[$key])) return self::$env[$key];
        return $default;
    }


    private static function getAllBeanFiles($dir)
    {
        $files = glob($dir . '/*');
        foreach ($files as $file) {
            if (is_dir($file)) {
                self::getAllBeanFiles($file);
            } else if (is_file($file) && pathinfo($file)['extension'] == 'php') {
                require_once($file);
            }

        }
    }


    //1、定义并加载app/controller(命名空间)下的所有php

    /**
     * @return void
     * @throws \ReflectionException
     */
    public static function scanBeans($scanDir, string $scanRootNamespace)
    {

//        $files = glob($scanDir . '/*.php');   //用于指定匹配文件和目录
//        foreach ($files as $file) {  //加载所有php文件
//            require_once $file;
//        }

        //加载app下所有php文件
        self::getAllBeanFiles($scanDir);


        //1\按命名空间过滤,排除内置类,
        $reader = AnnotationReader::getInstance();

        //app\controller下带命名空间的字符串类名
        foreach (get_declared_classes() as $className) {
            if (strstr($className, $scanRootNamespace)) {
                //这块读取类的注解（类使用的是bean注解）
                foreach ($reader->getClassAnnotations($className) as $classAnnotation) {
                    if (!isset(self::$handler[$classAnnotation['name']])) continue; //没有注解就跳过

                    //handler的名称
                    $handler = self::$handler[$classAnnotation['name']];
                    //自己实例化本身，$container->get(); 这里修改类的属性
                    $instance = self::$container->get($className);
                    //传递instance，保证修改的属性或方法，最终实例化类的时候也修改了；
                    //处理属性注解
                    self::handlerPropAnnotations($classAnnotation['reflection'], $reader, $instance);
                    //处理方法注解
                    self::handlerMethodAnnotations($classAnnotation['reflection'], $reader, $instance);
                    //处理类注解
                    $handler(self::$container, $instance, $classAnnotation['arguments']);
                }
            }
        }
    }


    public static function getBean($name)
    {
        try {
            return self::$container->get($name);
        } catch (\Exception $e) {
            return false;
        }
    }

    public static function setBean($name, $val)
    {
        return self::$container->set($name, $val);
    }


    /**
     * 处理属性注解的方法
     * @param \ReflectionClass $reflection
     * @param AnnotationReader $reader
     * @param object $instance
     * @return void
     * @throws \ReflectionException
     */
    public static function handlerPropAnnotations(\ReflectionClass $reflection, AnnotationReader $reader, object $instance)
    {
        $props = $reflection->getProperties(); //获取反射对象所有属性
        foreach ($props as $prop) {

            //这里可能返回空数组，（属性没有注解时返回空数组）
            $propAnnotations = $reader->getPropertyAnnotations($reflection->getName(), $prop->getName());
            foreach ($propAnnotations as $propAnnotion) {
                if (!isset(self::$handler[$propAnnotion['name']])) continue; //没有注解就跳过
                self::$handler[$propAnnotion['name']]($prop, $instance, $propAnnotion['arguments']);
            }
        }
    }


    /**
     * 处理方法注解的方法
     * @param \ReflectionClass $reflection
     * @param AnnotationReader $reader
     * @param object $instance
     * @return void
     * @throws \ReflectionException
     */
    public static function handlerMethodAnnotations(\ReflectionClass $reflection, AnnotationReader $reader, object $instance)
    {
        $methods = $reflection->getMethods(); //获取反射对象所有属性
        foreach ($methods as $method) {
            //这里可能返回空数组，（属性没有注解时返回空数组）
            $methodAnnotations = $reader->getMethodAnnotations($reflection->getName(), $method->getName());
            foreach ($methodAnnotations as $methodAnnotation) {
                if (!isset(self::$handler[$methodAnnotation['name']])) continue; //没有注解就跳过
                self::$handler[$methodAnnotation['name']]($method, $instance, $methodAnnotation['arguments']);
            }
        }
    }
}